package com.lobov.entities;

import java.io.Serializable;

import com.lobov.utils.Compressor;

/**
 * Матрица - структура данных для хранения математической матрицы чисел двойной
 * точности
 * 
 */
public class Matrix extends Timer implements EntityIface, Serializable {

	private static final long serialVersionUID = 1L;

	/**
	 * создать разреженную матрицу
	 * 
	 * @param xlength
	 *            ширина матрицы
	 * @param ylength
	 *            высота матрицы
	 */
	public Matrix(int xlength, int ylength) {
		super();
		this.xlength = xlength;
		this.ylength = ylength;
		this.values = new double[xlength * ylength];
		this.xs = new int[xlength * ylength];
		this.ys = new int[xlength * ylength];
		this.compressed = false;
	}

	/**
	 * создать сжатую матрицу
	 * 
	 * @param xlength
	 *            ширина матрицы
	 * @param ylength
	 *            высота матрицы
	 */
	public Matrix(int numOfElements, int xlength, int ylength) {
		super();
		this.values = new double[numOfElements];
		this.xs = new int[numOfElements];
		this.ys = new int[numOfElements];
		this.xlength = xlength;
		this.ylength = ylength;
		this.compressed = true;
	}

	private int xlength;
	private int ylength;
	private double[] values;
	private int[] xs;
	private int[] ys;

	public boolean compressed;

	private String name;

	public int getXlength() {
		return xlength;
	}

	public void setXlength(int xlength) {
		this.xlength = xlength;
	}

	public int getYlength() {
		return ylength;
	}

	public void setYlength(int ylength) {
		this.ylength = ylength;
	}

	public double[] getValues() {
		return values;
	}

	public void setValues(double[] values) {
		this.values = values;
	}

	public int[] getXs() {
		return xs;
	}

	public void setXs(int[] xs) {
		this.xs = xs;
	}

	public int[] getYs() {
		return ys;
	}

	public void setYs(int[] ys) {
		this.ys = ys;
	}

	@Override
	public boolean isCompressed() {
		return compressed;
	}

	public void setCompressed(boolean isCompressed) {
		this.compressed = isCompressed;
	}

	@Override
	public String toString() {
		if (compressed) {
			return printCompressedToString();
		} else {
			return printUnCompressedToString();
		}
	}

	private String printUnCompressedToString() {
		StringBuilder out = new StringBuilder();
		if (xlength == 0 || ylength == 0) {
			return "Didn't define length by X and Y for matrix";
		}
		int num = 0;
		for (int y = 0; y < ylength; y++) {
			for (int x = 0; x < xlength; x++) {
				out.append(values[num]);
				out.append(" ");
				num++;
			}
			if (y < ylength - 1) {
				out.append("\n");
			}
		}
		return out.toString();
	}

	private String printCompressedToString() {
		StringBuilder out = new StringBuilder();
		if (xlength == 0 || ylength == 0) {
			return "Didn't define length by X and Y for matrix";
		}
		int numCompress = 0; // порядок в сжатом формате
		int numValues = values.length;
		for (int y = 0; y < ylength; y++) {
			for (int x = 0; x < xlength; x++) {
				if (numValues != 0 && xs[numCompress] == x
						&& ys[numCompress] == y) {
					out.append(values[numCompress]);
					numCompress++;
					numValues--;

				} else {
					out.append(0d);
				}
				out.append(" ");
			}
			if (y < ylength - 1) {
				out.append("\n");
			}
		}
		return out.toString();
	}

	@Override
	public double getElementByXY(int x, int y) throws Exception {
		if (x > xlength || y > ylength) {
			throw new Exception("В матрице " + name + " нет элемента с X = "
					+ x + " и Y = " + y);
		}
		if (!compressed) {
			int n = 0;
			for (int h = 0; h < ylength; h++) {
				for (int l = 0; l < xlength; l++) {
					if (l == x - 1 && h == y - 1) {
						return values[n];
					}
					n++;
				}
			}
		} else {
			for (int n = 0; n < values.length; n++) {
				// думаю можно оптимизировать еще (доставать не перебором а по
				// формуле), но для сраснения с MPI не критично
				if (xs[n] == x - 1 && ys[n] == y - 1) {
					return values[n];
				}
			}
			return 0;
		}
		throw new Exception("В матрице нет элемента с  X = " + x + " и Y = "
				+ y);
	}

	@Override
	public EntityIface compress() throws Exception {
		return Compressor.compressMatrix(this);
	}

	@Override
	public EntityIface deCompress() throws Exception {
		return Compressor.deCompressMatrix(this);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public double getElementByX(int x) throws Exception {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void setElementByX(int x, double value) throws Exception {
		// TODO Auto-generated method stub
	}

	@Override
	public void setElementByXY(int x, int y, double value) throws Exception {
		// TODO Auto-generated method stub
	}

}
